///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __InstancedEntity_H__
#define __InstancedEntity_H__

#include "OgreMovableObject.h"
#include "OgreNode.h"

namespace Ogre
{
	
	

	
	class _OgreExport InstancedEntity : public Ogre::MovableObject
	{
		friend class InstanceBatch;
		friend class InstanceBatchShader;
		friend class InstanceBatchHW;
		friend class InstanceBatchHW_VTF;
		friend class BaseInstanceBatchVTF;
	protected:
		uint16 mInstanceId; //Note it may change after defragmenting!
		bool mInUse;
		InstanceBatch *mBatchOwner;

		AnimationStateSet *mAnimationState;
		SkeletonInstance *mSkeletonInstance;
		Matrix4 *mBoneMatrices;	 //Local space
		Matrix4 *mBoneWorldMatrices; //World space
		unsigned long mFrameAnimationLastUpdated;

		InstancedEntity* mSharedTransformEntity;	//When not null, another InstancedEntity controls the skeleton
												
		//Used in conjunction with bone matrix lookup. Tells the number of the transform for
		//as arranged in the vertex texture
		uint16 mTransformLookupNumber;

		//Stores the master when we're the slave, store our slaves when we're the master
		typedef vector<InstancedEntity*>::type InstancedEntityVec;
		InstancedEntityVec mSharingPartners;

		//////////////////////////////////////////////////////////////////////////
		// Parameters used for local transformation offset information
		// The 
		//////////////////////////////////////////////////////////////////////////

		///Object position
		Vector3 mPosition;
		Vector3 mDerivedLocalPosition;
		///Object orientation
		Quaternion mOrientation;
		///Object scale
		Vector3 mScale;
		///The maximum absolute scale for all dimension
		Real mMaxScaleLocal;
		///Full world transform
		Matrix4 mFullLocalTransform;
		///Tells if mFullTransform needs an updated
		bool mNeedTransformUpdate;
		/// Tells if the animation world transform needs an update
		bool mNeedAnimTransformUpdate;
		/// Tells whether to use the local transform parameters
		bool mUseLocalTransform;


		//Returns number of matrices written to xform, assumes xform has enough space
		size_t getTransforms( Matrix4 *xform ) const;
		//Returns number of 32-bit values written
		size_t getTransforms3x4( float *xform ) const;

		//Returns true if this InstancedObject is visible to the current camera
		bool findVisible( Camera *camera ) const;

		//Creates/destroys our own skeleton, also tells slaves to unlink if we're destroying
		void createSkeletonInstance();
		void destroySkeletonInstance();

		//Just unlinks, and tells our master we're no longer sharing
		void unlinkTransform();

		//Called when a slave has unlinked from us
		void notifyUnlink( const InstancedEntity *slave );

		//Mark the transformation matrixes as dirty
		inline void markTransformDirty();

		/// Incremented count for next name extension
        static NameGenerator msNameGenerator;

	public:
		InstancedEntity( InstanceBatch *batchOwner, uint32 instanceID, InstancedEntity* sharedTransformEntity = NULL);
		virtual ~InstancedEntity();

		
		bool shareTransformWith( InstancedEntity *slave );

		
		void stopSharingTransform();

		InstanceBatch* _getOwner() const { return mBatchOwner; }

		const String& getMovableType(void) const;

		const AxisAlignedBox& getBoundingBox(void) const;
		Real getBoundingRadius(void) const;

		//This is used by our batch owner to get the closest entity's depth, returns infinity
		//when not attached to a scene node
		Real getSquaredViewDepth( const Camera* cam ) const;

		//Overriden so we can tell the InstanceBatch it needs to update it's bounds
		void _notifyMoved(void);
		void _notifyAttached( Node* parent, bool isTagPoint = false );

		//Do nothing, InstanceBatch takes care of this.
		void _updateRenderQueue( RenderQueue* queue )	{}
		void visitRenderables( Renderable::Visitor* visitor, bool debugRenderables = false ) {}

		bool hasSkeleton(void) const { return mSkeletonInstance != 0; }
		
		SkeletonInstance* getSkeleton(void) const { return mSkeletonInstance; }

		AnimationState* getAnimationState(const String& name) const;
		
		AnimationStateSet* getAllAnimationStates(void) const;

		virtual bool _updateAnimation(void);

		void setTransformLookupNumber(uint16 num) { mTransformLookupNumber = num;}

		const Vector3& getPosition() const { return mPosition; }
		 
		void setPosition(const Vector3& position, bool doUpdate = true);

		const Quaternion& getOrientation() const { return mOrientation; }
		
		void setOrientation(const Quaternion& orientation, bool doUpdate = true);

		const Vector3& getScale() const { return mScale; }
		 
		void setScale(const Vector3& scale, bool doUpdate = true);

		Real getMaxScaleCoef() const;

		void updateTransforms();

		bool isInUse() const { return mInUse; }
		
		void setInUse(bool used);

		virtual const Matrix4& _getParentNodeFullTransform(void) const { 
			assert((!mNeedTransformUpdate || !mUseLocalTransform) && "Tranform data should be updated at this point");
			return mUseLocalTransform ? mFullLocalTransform :
				mParentNode ? mParentNode->_getFullTransform() : Matrix4::IDENTITY;
		}

		const Vector3& _getDerivedPosition() const {
			assert((!mNeedTransformUpdate || !mUseLocalTransform) && "Tranform data should be updated at this point");
			return mUseLocalTransform ? mDerivedLocalPosition :
				mParentNode ? mParentNode->_getDerivedPosition() : Vector3::ZERO;
		}

		
		virtual bool isInScene(void) const
		{
			//We assume that the instanced entity is in the scene if it is in use
			//It is in the scene whether it has a parent node or not
			return mInUse;
		}



	};
}

#endif
